package org.springblade.modules.admin.controller.suger;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.ElevatorMaintainStatusDay;
import org.springblade.modules.admin.enums.MaintainPlanContentEnum;
import org.springblade.modules.admin.enums.TaskStatusEnum;
import org.springblade.modules.admin.service.BIService;
import org.springblade.modules.admin.service.BuildingService;
import org.springblade.modules.admin.service.ElevatorService;
import org.springblade.modules.admin.service.IElevatorMaintainStatusDayService;
import org.springblade.modules.admin.vo.BiBuildingVO;
import org.springblade.modules.admin.vo.sugar.bi.BIScreenElevatorPlanPartsVO;
import org.springblade.modules.admin.vo.sugar.bi.BIScreenElevatorVO;
import org.springblade.modules.admin.vo.sugar.bi.BIScreenElevatorPlanVO;
import org.springblade.modules.admin.vo.sugar.bi.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Date 8:42 2024/12/11
 * @Description {
 *   BI数据接口
 * }
 * @Author lsay
 **/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/bi/screen")
public class BIScreenController {

	private final BIService biService;
	private final BuildingService buildingService;
	private final ElevatorService elevatorService;
	private final IElevatorMaintainStatusDayService maintainStatusDayService;

	/**
	 * @Author lsay
	 * @Description {
	 *   项目电梯信息
	 * }
	 * @Date 10:51 2024/12/12
	 * @Param
	 * @return
	 **/
	@GetMapping("/building/elevator")
	public R<List<BIScreenElevatorVO>> selectBuildingElevator(String startDate, String endDate){
		return R.data(biService.selectBuildingElevator(startDate,endDate));
	}

	/**
	 * @Author lsay
	 * @Description {
	 *   项目电梯维修工单信息
	 * }
	 * @Date 10:51 2024/12/12
	 * @Param
	 * @return
	 **/
	@GetMapping("/building/elevator/wx")
	public R<List<BIScreenElevatorPlanVO>> selectBuildingElevatorWx(String startDate, String endDate){
		return R.data(biService.selectBuildingElevatorWx(startDate,endDate));
	}

	/**
	 * @Author lsay
	 * @Description {
	 *   项目电梯维修工单信息
	 * }
	 * @Date 10:51 2024/12/12
	 * @Param
	 * @return
	 **/
	@GetMapping("/building/elevator/wx/work")
	public R<List<BIScreenElevatorPlanPartsVO>> selectBuildingElevatorWxWork(String startDate, String endDate,Integer planType){
		return R.data(biService.selectBuildingElevatorWxWork(startDate,endDate,planType));
	}

	@TenantIgnore
	@GetMapping("/building")
	public R<List<BiBuildingVO>> selectBuilding(){
		return R.data(buildingService.listBiBuildingList());
	}

	@GetMapping("/rescue/big/table")
	@ApiOperation("天雄飞书救援宽表excel")
	@ApiOperationSupport(order = 40)
	public void rescueBigTableExcel(Date startDate, Date endDate,HttpServletResponse response) throws IOException {
		// 1. 获取天雄所需的银河租户救援宽表
		//获取 plan id 列表
		List<BILarkRescueBigTableVO> records = buildingService.getLarkRescueBigTable(startDate, endDate);

		// 2. 数据处理, 将一些秒/毫秒的时间转换成分钟，将任务状态值转换成中文
		records.forEach( r -> {
			// 将 sendUseTime 秒值转换成分钟
			Integer sendUseTime = r.getSendUseTime();
			if(!Func.isNull(sendUseTime)){
				double sendUseTimeMinuteValue = ((double) sendUseTime / 60);
				double sendUseTimeMinute = getRounder(sendUseTimeMinuteValue);
				r.setSendUseTimeMinute(sendUseTimeMinute);
			}
			// 将毫秒值转换成分钟，以下同理
			Long arriveDuration = r.getArriveDuration();
			if(!Func.isNull(arriveDuration)){
				double arriveUseTimeMinuteValue = ((double) arriveDuration / 60000);
				double arriveUseTimeMinute = getRounder(arriveUseTimeMinuteValue);
				r.setArriveUseTime(arriveUseTimeMinute);
			}

			Double rescueUseTime = r.getRescueTime();
			if(!Func.isNull(rescueUseTime)){
				double rescueUseTimeMinuteValue = (rescueUseTime / 60);
				double rescueUseTimeMinute = getRounder(rescueUseTimeMinuteValue);
				r.setRescueTime(rescueUseTimeMinute);
			}

			Long workMill = r.getWorkMill();
			if(!Func.isNull(workMill)){
				double workMillValue = ((double) workMill / 60000);
				double workMinute = getRounder(workMillValue);
				r.setWorkMinute(workMinute);
			}

			Integer planStatus = r.getPlanStatus();
			if(!Func.isNull(planStatus)){
				r.setPlanStatusDesc(TaskStatusEnum.getTaskName(MaintainPlanContentEnum.MAINTAIN_KR.getId(), planStatus));
			}

		});

		//导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("任务编号","电梯ID", "电梯位置", "项目名称", "项目类型", "省", "市", "区", "项目要求到达时长", "任务指派",
					"实际执行人", "人员所在维保组", "电梯所在维保组", "报警人", "报警来源", "报警时间", "人员指派时间", "签到时间（第一次）",
					"签退时间（最后1次）", "救援完成时间", "调度用时（分）", "到达用时（分）","救援作业用时（分）", "救援整体耗时（分）", "任务状态",
					"救援梯是否在保", "救援梯是否外包"
				);
			rows.add(title);
			if (Func.isNotEmpty(records)) {
				for (BILarkRescueBigTableVO s : records) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getPlanId()));
					row.add(Convert.toStr(processRow(s.getElevatorId())));
					row.add(Convert.toStr(processRow(s.getElevatorAddress())));
					row.add(Convert.toStr(processRow(s.getBuildingName())));
					row.add(Convert.toStr(processRow(s.getBuildingType())));
					row.add(Convert.toStr(processRow(s.getProvince())));
					row.add(Convert.toStr(processRow(s.getCity())));
					row.add(Convert.toStr(processRow(s.getAd())));
					row.add(Convert.toStr(processRow(s.getRequireTime())));
					row.add(Convert.toStr(processRow(s.getPlanUsers())));
					row.add(Convert.toStr(processRow(s.getExeUsers())));
					row.add(Convert.toStr(processRow(s.getUserGroups())));
					row.add(Convert.toStr(processRow(s.getElevatorMaintainGroup())));
					row.add(Convert.toStr(processRow(s.getReportName())));
					row.add(Convert.toStr(processRow(s.getReportSource())));
					row.add(Convert.toStr(timeFormat(s.getReportTime())));
					row.add(Convert.toStr(timeFormat(s.getSendTime())));
					row.add(Convert.toStr(timeFormat(s.getFirstSignTime())));
					row.add(Convert.toStr(timeFormat(s.getLastSignTime())));
					row.add(Convert.toStr(timeFormat(s.getRescueFinishTime())));
					row.add(Convert.toStr(processRow(String.valueOf(s.getSendUseTimeMinute()))));
					row.add(Convert.toStr(processRow(String.valueOf(s.getArriveUseTime()))));
					row.add(Convert.toStr(processRow(String.valueOf(s.getRescueTime()))));
					row.add(Convert.toStr(processRow(String.valueOf(s.getWorkMinute()))));
					row.add(Convert.toStr(processRow(s.getPlanStatusDesc())));
					row.add(Convert.toStr(s.getIsInMaintain()?"是":"否"));
					row.add(Convert.toStr(s.getIsOutsource()?"是":"否"));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/health/statistics")
	@ApiOperation("天雄飞书救援健康统计excel")
	@ApiOperationSupport(order = 41)
	public void rescueHealthStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if(Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		// 2025-01-24 修改 时间处理：去掉周描述；
		List<BILarkRescueCountSourceVO> getGalaxyRescueResource = biService.getGalaxyRescueResource(startDate, endDate);
		Map<String, List<BILarkRescueCountSourceVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = getGalaxyRescueResource.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getCreateTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = getGalaxyRescueResource.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "yyyy/M/d"))));
		} else {
			dateGroup = getGalaxyRescueResource.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getCreateTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		// 分组时间按分组倒序/正序
		List<String> dateList = getDateList(startDate, endDate, dateType);
		List<BILarkRescueHealthVO> exportData = new ArrayList<>();
		dateList.forEach(d -> {
			if(dateGroup.containsKey(d)){
				List<BILarkRescueCountSourceVO> v = dateGroup.get(d);
				// 处理日期描述
				String dateDesc = null;
				Date rangeEndDate = null;
				int elevatorCount = 0;
				if(Func.isNotEmpty(v)){
					Date groupCreateTimeDemo = v.get(0).getCreateTime();
					rangeEndDate = getRangeEndDate(groupCreateTimeDemo, dateType);
					dateDesc = d;
				}
				// 根据 rangeEndDate 获取监控梯台数
				if(!Func.isNull(rangeEndDate)){
//					elevatorCount = elevatorService.getMonitorElevatorCount(rangeEndDate);
					// 2025-02-05 将监控梯台数修改成 在保非外包
					// 将rangeEndDate 改为 "yyyy-MM-dd" 格式
					String rangeEndDateFormat = DateUtil.format(rangeEndDate, "yyyy-MM-dd");
					// 将 rangeEndDateFormat 改成 date
					Date rangeEndDateParam = DateUtil.parse(rangeEndDateFormat);
					elevatorCount = maintainStatusDayService.countByRangeEndDate(rangeEndDateParam);
				}
				BILarkRescueHealthVO rescueHealthVO = new BILarkRescueHealthVO();
				rescueHealthVO.setGroupDateDesc(dateDesc);
				rescueHealthVO.setElevatorCount(elevatorCount);
				rescueHealthVO.setRescueCount(v.size());
				long onTimeCount = v.stream().filter(e -> new Integer(3).equals(e.getIsOverTime())).count();
				rescueHealthVO.setOnTimeCount((int) onTimeCount);
				rescueHealthVO.setLateCount(v.size() - (int) onTimeCount);
				exportData.add(rescueHealthVO);
			}
		});

		//导出操作

		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "监控梯台数", "累计救援次数", "准时到达救援次数", "超项目指定时长次数");
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkRescueHealthVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(s.getElevatorCount()));
					row.add(Convert.toStr(s.getRescueCount()));
					row.add(Convert.toStr(s.getOnTimeCount()));
					row.add(Convert.toStr(s.getLateCount()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/time/use/statistics")
	@ApiOperation("天雄飞书救救援耗时excel")
	@ApiOperationSupport(order = 42)
	public void rescueTimeUseStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if(Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		List<BILarkRescueBigTableVO> records = buildingService.getLarkRescueBigTableByCondition(startDate, endDate);
		Map<String, List<BILarkRescueBigTableVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getReportTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getReportTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getReportTime()), "yyyy/M/d"))));
		} else {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getReportTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		// 2025-01-24 时间处理，去掉周描述； 时间分组倒序/正序
		List<BILarkRescueUseTimeVO> exportData = new ArrayList<>();
		List<String> dateList = getDateList(startDate, endDate, dateType);
		dateList.forEach(d -> {
			if(dateGroup.containsKey(d)){
				List<BILarkRescueBigTableVO> v = dateGroup.get(d);
				if(Func.isEmpty(v)){
					return;
				}
				v.forEach(e -> {
					if(Func.isNull(e.getRescueTime())){
						e.setRescueTime((double) 0);
					}
				});
				// 处理日期描述
				String dateDesc = null;
				if(Func.isNotEmpty(v)){
					dateDesc = d;
				}
				// 调度耗时处理：统计调度耗时总数/数量, 即列表里sendUseTime字段总和（秒）
				double avgSendTimeSecond = v.stream().mapToDouble(BILarkRescueBigTableVO::getSendUseTime).average().orElse(0);
				double avgSendTimeValue = avgSendTimeSecond/60;
				double avgSendTime = getRounder(avgSendTimeValue);
				// 调度耗时处理：统计调度耗时中位数, 即列表里avgSendTime字段中位数，先按升序排序，再取中间值
				double middleSendTimeValue = (double) v.stream().sorted(Comparator.comparing(BILarkRescueBigTableVO::getSendUseTime)).collect(Collectors.toList()).get(v.size() / 2).getSendUseTime() /60;
				double middleSendTime = getRounder(middleSendTimeValue);
				// 到达耗时处理，统计到达耗时总数/数量, 即列表里arriveDuration字段总和（毫秒）
				double avgArriveTimeValue = v.stream().mapToDouble(BILarkRescueBigTableVO::getArriveDuration).average().orElse(0)/60/1000;
				double avgArriveTime = getRounder(avgArriveTimeValue);
				// 到达耗时处理，统计到达耗时中位数, 即列表里avgArriveTime字段中位数，先按升序排序，再取中间值
				double middleArriveTimeValue = (double) v.stream().sorted(Comparator.comparing(BILarkRescueBigTableVO::getArriveDuration)).collect(Collectors.toList()).get(v.size() / 2).getArriveDuration() /60/1000;
				double middleArriveTime = getRounder(middleArriveTimeValue);
				// 救援耗时处理，统计救援耗时总数/数量, 即列表里rescueTime字段总和（秒）
				double avgRescueTimeSecond = v.stream().mapToDouble(BILarkRescueBigTableVO::getRescueTime).average().orElse(0);
				double avgRescueTimeValue = avgRescueTimeSecond/60;
				double avgRescueTime = getRounder(avgRescueTimeValue);
				// 救援耗时处理，统计救援耗时中位数, 即列表里avgRescueTime字段中位数，先按升序排序，再取中间值
				double middleRescueTimeValue = v.stream().sorted(Comparator.comparing(BILarkRescueBigTableVO::getRescueTime)).collect(Collectors.toList()).get(v.size()/2).getRescueTime()/60;
				double middleRescueTime = getRounder(middleRescueTimeValue);
				// 救援整体耗时处理，统计救援整体耗时，即列表里workMill字段总和（毫秒）
				int size = v.size();
				double avgTotalTimeValue = v.stream().mapToDouble(BILarkRescueBigTableVO::getWorkMill).sum()/60/1000/size;
				double avgTotalTime = getRounder(avgTotalTimeValue);
				// 救援整体耗时处理，统计救援整体耗时中位数, 即列表里totalRescueTime字段中位数，先按升序排序，再取中间值
				double middleTotalTimeValue = (double) v.stream().sorted(Comparator.comparing(BILarkRescueBigTableVO::getWorkMill)).collect(Collectors.toList()).get(v.size() / 2).getWorkMill() /60/1000;
				double middleTotalTime = getRounder(middleTotalTimeValue);
				// 封装导出数据
				BILarkRescueUseTimeVO rescueUseTimeVO = new BILarkRescueUseTimeVO();
				rescueUseTimeVO.setGroupDateDesc(dateDesc);
				rescueUseTimeVO.setAvgSendTime(avgSendTime);
				rescueUseTimeVO.setMiddleSendTime(middleSendTime);
				rescueUseTimeVO.setAvgArriveTime(avgArriveTime);
				rescueUseTimeVO.setMiddleArriveTime(middleArriveTime);
				rescueUseTimeVO.setAvgRescueTime(avgRescueTime);
				rescueUseTimeVO.setMiddleRescueTime(middleRescueTime);
				rescueUseTimeVO.setAvgTotalTime(avgTotalTime);
				rescueUseTimeVO.setMiddleTotalTime(middleTotalTime);
				exportData.add(rescueUseTimeVO);
			}
		});
		// 导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "平均调度耗时（分）", "调度耗时中位数（分）", "到达平均耗时（分）", "到达平均耗时中位数（分）",
					"救援平均耗时（分）", "救援平均耗时中位数（分）", "救援整体平均耗时（分）", "救援整体平均耗时中位数（分）"
				);
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkRescueUseTimeVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(s.getAvgSendTime()));
					row.add(Convert.toStr(s.getMiddleSendTime()));
					row.add(Convert.toStr(s.getAvgArriveTime()));
					row.add(Convert.toStr(s.getMiddleArriveTime()));
					row.add(Convert.toStr(s.getAvgRescueTime()));
					row.add(Convert.toStr(s.getMiddleRescueTime()));
					row.add(Convert.toStr(s.getAvgTotalTime()));
					row.add(Convert.toStr(s.getMiddleTotalTime()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/group/statistics")
	@ApiOperation("天雄飞书救救援维保组excel")
	@ApiOperationSupport(order = 43)
	public void rescueGroupStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if (Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		List<BILarkGroupSourceVO> records = buildingService.getLarkGroupSourceByCondition(startDate, endDate);
		Map<String, List<BILarkGroupSourceVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getCreateTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "yyyy/M/d"))));
		} else {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getCreateTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		// 2025-01-24 时间处理，去掉周描述； 时间分组倒序/正序
		List<String> dateList = getDateList(startDate, endDate, dateType);
		List<BILarkGroupVO> exportData = new ArrayList<>();
		dateList.forEach(d -> {
			if(dateGroup.containsKey(d)) {
				List<BILarkGroupSourceVO> v = dateGroup.get(d);
				if (Func.isEmpty(v)) {
					return;
				}
				String dateDesc;
				if (Func.isNotEmpty(v)) {
					Date groupCreateTimeDemo = v.get(0).getCreateTime();
					dateDesc = d;
				} else {
					dateDesc = null;
				}
				// v 根据 deptName 分组
				v.forEach(e -> {
					if(Func.isNull(e.getDeptName())){
						e.setDeptName("电梯未知");
					}
				});
				Map<String, List<BILarkGroupSourceVO>> deptGroup = v.stream().collect(Collectors.groupingBy(BILarkGroupSourceVO::getDeptName));
				deptGroup.forEach((deptName, deptList) -> {
					BILarkGroupVO groupVO = new BILarkGroupVO();
					groupVO.setGroupDateDesc(dateDesc);
					groupVO.setDeptName(deptName);
					groupVO.setGroupElevatorCount(deptList.get(0).getGroupElevatorCount());
					groupVO.setRescueTime(deptList.size());
					exportData.add(groupVO);
				});
			}
		});
		// 导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "维保组", "责任梯台数", "累计救援次数");
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkGroupVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(processRow(s.getDeptName())));
					row.add(Convert.toStr(s.getGroupElevatorCount()));
					row.add(Convert.toStr(s.getRescueTime()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/building/statistics")
	@ApiOperation("天雄飞书救救援楼栋excel")
	@ApiOperationSupport(order = 44)
	public void rescueBuildingStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if (Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		List<BILarkRescueBuildingSourceVO> records = buildingService.getLarkRescueBuildingSourceByCondition(startDate, endDate);
		Map<String, List<BILarkRescueBuildingSourceVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getCreateTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "yyyy/M/d"))));
		} else {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getCreateTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		List<BILarkRescueBuildingVO> exportData = new ArrayList<>();
		// 2025-01-24 时间处理，去掉周描述； 时间分组倒序/正序
		List<String> dateList = getDateList(startDate, endDate, dateType);
		dateList.forEach(d -> {
			if(dateGroup.containsKey(d)) {
				List<BILarkRescueBuildingSourceVO> v = dateGroup.get(d);
				if (Func.isEmpty(v)) {
					return;
				}
				// 处理日期描述
				String dateDesc;
				Date rangeEndDate = null;
				if (Func.isNotEmpty(v)) {
					Date groupCreateTimeDemo = v.get(0).getCreateTime();
					rangeEndDate = getRangeEndDate(groupCreateTimeDemo, dateType);
					dateDesc = d;
				} else {
					dateDesc = null;
				}
				// v 根据 buildingName 分组
				v.forEach(e -> {
					if(Func.isNull(e.getBuildingName())){
						e.setBuildingName("电梯未知");
					}
				});
				Map<String, List<BILarkRescueBuildingSourceVO>> buildingGroup = v.stream().collect(Collectors.groupingBy(BILarkRescueBuildingSourceVO::getBuildingName));
				buildingGroup.forEach((buildingName, buildingList) -> {
					BILarkRescueBuildingVO buildingVO = new BILarkRescueBuildingVO();
					buildingVO.setGroupDateDesc(dateDesc);
					buildingVO.setBuildingName(buildingName);
					buildingVO.setElevatorCount(buildingList.get(0).getElevatorCount());
					buildingVO.setRescueCount(buildingList.size());
					exportData.add(buildingVO);
				});
			}
		});
		// 导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "项目名称", "电梯数", "累计救援次数");
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkRescueBuildingVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(processRow(s.getBuildingName())));
					row.add(Convert.toStr(s.getElevatorCount()));
					row.add(Convert.toStr(s.getRescueCount()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/area/statistics")
	@ApiOperation("天雄飞书救救援区域excel")
	@ApiOperationSupport(order = 45)
	public void rescueAreaStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if (Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		List<BILarkRescueAreaResourceVO> records = buildingService.getLarkRescueAreaResourceByCondition(startDate, endDate);
		Map<String, List<BILarkRescueAreaResourceVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getCreateTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "yyyy/M/d"))));
		} else {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getCreateTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		// 2025-01-24 时间处理，去掉周描述； 时间分组倒序/正序
		List<String> dateList = getDateList(startDate, endDate, dateType);
		List<BILarkRescueAreaVO> exportData = new ArrayList<>();
		dateList.forEach(d ->{
			if(dateGroup.containsKey(d)){
				List<BILarkRescueAreaResourceVO> v = dateGroup.get(d);
				if (Func.isEmpty(v)) {
					return;
				}
				// 处理日期描述
				String dateDesc;
				if (Func.isNotEmpty(v)) {
					dateDesc = d;
				} else {
					dateDesc = null;
				}
				// v 根据 areaName 分组
				v.forEach(e -> {
					if(Func.isNull(e.getAdName())){
						e.setAdName("电梯未知");
					}
				});
				Map<String, List<BILarkRescueAreaResourceVO>> areaGroup = v.stream().collect(Collectors.groupingBy(BILarkRescueAreaResourceVO::getAdName));
				areaGroup.forEach((areaName, areaList) -> {
					BILarkRescueAreaVO areaVO = new BILarkRescueAreaVO();
					areaVO.setGroupDateDesc(dateDesc);
					areaVO.setAdName(areaName);
					areaVO.setElevatorCount(areaList.get(0).getElevatorCount());
					areaVO.setRescueCount(areaList.size());
					exportData.add(areaVO);
				});
			}
		});
		// 导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "行政区域", "电梯数", "累计救援次数");
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkRescueAreaVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(processRow(s.getAdName())));
					row.add(Convert.toStr(s.getElevatorCount()));
					row.add(Convert.toStr(s.getRescueCount()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	@GetMapping("/rescue/use/place/statistics")
	@ApiOperation("天雄飞书救救援使用场所excel")
	@ApiOperationSupport(order = 46)
	public void rescueUsePlaceStatisticsExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		if (Func.isNull(startDate) || Func.isNull(endDate) || Func.isNull(dateType)) {
			throw new ServiceException("参数不能为空");
		}
		startDate = getRangeStartDate(startDate, dateType);
		endDate = getRangeEndDate(endDate, dateType);
		List<BILarkRescueAreaResourceVO> records = buildingService.getLarkRescueAreaPlaceByCondition(startDate, endDate);
		Map<String, List<BILarkRescueAreaResourceVO>> dateGroup;
		if (new Integer(1).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(e -> DateUtil.format(e.getCreateTime(), "yyyy/M/d")));
		} else if (new Integer(2).equals(dateType)) {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(t.getCreateTime()), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(t.getCreateTime()), "yyyy/M/d"))));
		} else {
			dateGroup = records.stream().collect(Collectors.groupingBy(t -> String.format("%s", DateUtil.format(t.getCreateTime(), "yyyy/M"))));
		}
		if(Func.isEmpty(dateGroup)){
			throw new ServiceException("该查询范围下无数据，无需导出");
		}
		// 2025-01-24 时间处理，去掉周描述； 时间分组倒序/正序
		List<String> dateList = getDateList(startDate, endDate, dateType);
		List<BILarkRescueAreaVO> exportData = new ArrayList<>();
		dateList.forEach(d -> {
			if (dateGroup.containsKey(d)) {
				List<BILarkRescueAreaResourceVO> v = dateGroup.get(d);
				if (Func.isEmpty(v)) {
					return;
				}
				if (Func.isEmpty(v)) {
					return;
				}
				// 处理日期描述
				String dateDesc;
				if (Func.isNotEmpty(v)) {
					dateDesc = d;
				} else {
					dateDesc = null;
				}
				// v 根据 areaName 分组
				v.forEach(e -> {
					if (Func.isNull(e.getAdName())) {
						e.setAdName("电梯未知");
					}
				});
				Map<String, List<BILarkRescueAreaResourceVO>> areaGroup = v.stream().collect(Collectors.groupingBy(BILarkRescueAreaResourceVO::getAdName));
				areaGroup.forEach((areaName, areaList) -> {
					BILarkRescueAreaVO areaVO = new BILarkRescueAreaVO();
					areaVO.setGroupDateDesc(dateDesc);
					areaVO.setAdName(areaName);
					areaVO.setElevatorCount(areaList.get(0).getElevatorCount());
					areaVO.setRescueCount(areaList.size());
					exportData.add(areaVO);
				});
			}
		});
		// 导出操作
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("日期", "使用场所", "电梯数", "累计救援次数");
			rows.add(title);
			if (Func.isNotEmpty(exportData)) {
				for (BILarkRescueAreaVO s : exportData) {
					List<String> row = new ArrayList<>(16);
					row.add(Convert.toStr(s.getGroupDateDesc()));
					row.add(Convert.toStr(processRow(s.getAdName())));
					row.add(Convert.toStr(s.getElevatorCount()));
					row.add(Convert.toStr(s.getRescueCount()));
					rows.add(row);
				}
			}
			String fileName =DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			log.error("导出excel失败: ", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}

	}

	/**
	 * 功能描述: 发送响应流方法
	 */
	private void setResponseHeader(HttpServletResponse response, String fileName) {
		try {
			try {
				fileName = new String(fileName.getBytes(), "ISO8859-1");
			} catch (UnsupportedEncodingException e) {
				log.error("文件名编码异常: ", e);
			}
			response.setContentType("application/octet-stream;charset=ISO8859-1");
			response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
			response.addHeader("Pargam", "no-cache");
			response.addHeader("Cache-Control", "no-cache");
		} catch (Exception ex) {
			log.error("设置响应流头部失败: ", ex);
		}
	}

	private String processRow(String rowData){
		if(Func.isNotBlank(rowData)){
			if("null".equals(rowData)){
				return "";
			}
			return rowData;
		}
		return "";
	}

	private String timeFormat(Date date){
		if(Func.isNull(date)){
			return "";
		}
		return DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
	}

	private String getDateDesc(String key, Date date, Integer dateType){

		if(new Integer(1).equals(dateType)){
			return key;
		}
		if(new Integer(2).equals(dateType)){
            return DateUtil.weekOfYear(date) +
				"周" +
				"(" +
				key +
				")";
		}
		return key;
	}

	private Date getRangeStartDate(Date startDate, Integer dateType){
		if (new Integer(2).equals(dateType)) {
			startDate = DateUtil.beginOfWeek(startDate);

		} else if (new Integer(3).equals(dateType)) {
			startDate = DateUtil.beginOfMonth(startDate);
		}
		return startDate;
	}

	private Date getRangeEndDate(Date endDate, Integer dateType){
		if (new Integer(2).equals(dateType)) {
			endDate = DateUtil.endOfWeek(endDate);
		} else if (new Integer(3).equals(dateType)) {
			endDate = DateUtil.endOfMonth(endDate);
		}
		return endDate;
	}
	/**
	 * 维修健康指标表格
	 */
	@TenantIgnore
	@GetMapping("/wx/health/index")
	public void downWxHealthIndexExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxHealthIndexExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	/**
	 * 维修工时汇总
	 */
	@TenantIgnore
	@GetMapping("/wx/work/time")
	public void downWxWorkTimeExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxWorkTimeExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	/**
	 * 维修人员工时汇总
	 */
	@TenantIgnore
	@GetMapping("/wx/user/work/time")
	public void downWxUserWorkTimeExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxUserWorkTimeExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	/**
	 * 维保小组故障率
	 */
	@TenantIgnore
	@GetMapping("/wx/elevator/dept")
	public void downWxElevatorDeptExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxElevatorDeptExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	/**
	 * 维保人员故障率
	 */
	@TenantIgnore
	@GetMapping("/wx/elevator/user")
	public void downWxElevatorUserExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxElevatorUserExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	/**
	 * 报障停梯指标
	 */
	@TenantIgnore
	@GetMapping("/wx/elevator/stop")
	public void downWxElevatorStopExcel(Date startDate, Date endDate, Integer dateType, HttpServletResponse response) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			biService.downWxElevatorStopExcel(startDate,endDate,dateType,rows);
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}

	private double getRounder(double value){
		return Math.round(value * 100.0) / 100.0;
	}

	private List<String> getDateList(Date startDate, Date endDate, Integer dateType){
		List<String> dateList = new ArrayList<>(32);
		if (dateType.equals(2)) {
			startDate = DateUtil.beginOfWeek(startDate);
			endDate = DateUtil.endOfWeek(endDate);
			// 周
			List<DateTime> rangeDate = DateUtil.rangeToList(startDate, endDate, DateField.DAY_OF_YEAR);
			rangeDate.forEach(date -> {
				String weekDate = String.format("%s~%s", DateUtil.format(DateUtil.beginOfWeek(date), "yyyy/M/d"), DateUtil.format(DateUtil.endOfWeek(date), "yyyy/M/d"));
				if (!dateList.contains(weekDate)) {
					dateList.add(weekDate);
				}
			});
		} else if (dateType.equals(3)) {
			startDate = DateUtil.beginOfMonth(startDate);
			endDate = DateUtil.endOfMonth(endDate);
			// 月
			List<DateTime> rangeDate = DateUtil.rangeToList(startDate, endDate, DateField.MONTH);
			rangeDate.forEach(date -> {
				String monthDate = DateUtil.format(date, "yyyy/M");
				if (!dateList.contains(monthDate)) {
					dateList.add(monthDate);
				}
			});
		}
		Collections.reverse(dateList);
		return dateList;
	}

}
